/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "rs_render_process_manager.h"

#include <mutex>

#include "render_process/rs_render_process.h"
#include "rs_profiler.h"
#include "rs_trace.h"
#include "../../../include/screen_manager/screen_types.h"

namespace OHOS {
namespace Rosen {
constexpr const char* CMD_SEP = " ";
constexpr int32_t PIPE_FD_NUM = 2;
constexpr int32_t PIPE_OUT = 0;
constexpr int32_t PIPE_IN = 1;

RSRenderProcessManager::RSRenderProcessManager(sptr<RSScreenManager> screenManager)
{
    screenManager_ = screenManager;
    ScreenChangeCallback connectCallback = [this](ScreenId id, ScreenEvent event, ScreenChangeReason reason = ScreenChangeReason::DEFAULT) {
        // Fork ScreenId对应子进程
        std::string out;
        ForkExec("/system/bin/render_process", id, &out);
        return;
    };
    screenHandler_ = new RSScreenChangeHandler(connectCallback);
    screenManager_->AddScreenChangeCallback(screenHandler_);
}

inline std::vector<std::string> RSRenderProcessManager::Split(const std::string& str, const std::string& sep)
{
    std::string s = str;
    std::vector<std::string> res;
    while (!s.empty()) {
        size_t pos = s.find(sep);
        if (pos == std::string::npos) {
            res.emplace_back(s);
            break;
        }
        res.emplace_back(s.substr(0, pos));
        s = s.substr(pos + sep.size());
    }
    return res;
}

std::vector<const char*> RSRenderProcessManager::FormatCmd(const std::vector<std::string>& cmd)
{
    std::vector<const char*> res;
    res.reserve(cmd.size() + 1);

    // string is converted to char* and the result is saved in res
    std::transform(cmd.begin(), cmd.end(), std::back_inserter(res), [](const std::string& str) { return str.c_str(); });
    res.emplace_back(nullptr);
    return res;
}

int32_t RSRenderProcessManager::ForkExecChildProcess(const int32_t* pipeFd, int32_t count, const std::vector<const char*>& args)
{
    RS_LOGW("Fork OK");
    if (count != PIPE_FD_NUM) {
        RS_LOGE("Fork exec parent process failed");
        _exit(-1);
    }
    RS_LOGW("Fork done and ready to close");
    if (close(pipeFd[PIPE_OUT]) != 0) {
        RS_LOGE("Fork close failed, errorno: %{public}d, errormsg: %{public}s", errno, strerror(errno));
        _exit(-1);
    }
    RS_LOGW("Fork close done and ready for dup2");
    if (dup2(pipeFd[PIPE_IN], STDOUT_FILENO) == -1) {
        RS_LOGE("Fork dup2 failed, errorno: %{public}d, errormsg: %{public}s", errno, strerror(errno));
        _exit(-1);
    }
    RS_LOGW("Fork ready for execv");
    if (execv(args[0], const_cast<char* const*>(&args[0])) == -1) {
        RS_LOGE("Fork execv command failed, errorno: %{public}d, errormsg: %{public}s", errno, strerror(errno));
    }
    RS_LOGW("Fork execv done");
    if (close(pipeFd[PIPE_IN]) != 0) {
        RS_LOGE("Fork close failed, errorno: %{public}d, errormsg: %{public}s", errno, strerror(errno));
        _exit(-1);
    }
    _exit(-1);
}

int32_t RSRenderProcessManager::ForkExec(const std::string& command, ScreenId screenId, std::string* out)
{
    const std::vector<std::string> cmd = Split(command, CMD_SEP);
    std::vector<const char*> args = FormatCmd(cmd);
    int32_t pipeFd[PIPE_FD_NUM] = {0};
    if (pipe(pipeFd) < 0) {
        RS_LOGE("Create pipe failed, errorno: %{public}d, errornomsg: %{public}s", errno, strerrno(errno));
        return -1;
    }
    RS_LOGW("ForkExec");
    pid_t pid = fork();
    RS_LOGW("ForkDone %{public}d", pid);
    if (pid < 0) {
        RS_LOGE("Fork failed, errorno: %{public}d, errornomsg: %{public}s", errno, strerrno(errno));
        return -1;
    }
    if (pid == 0) {
        RS_LOGW("Fork begin %{public}d", getpid());
        ForkExecChildProcess(pipeFd, PIPE_FD_NUM, args);

        // 设置screenId对应子进程pid
        screenId2RenderProcess_[screenId] getpid();

        return -1;
    } else {
        RS_LOGW("Fork error %{public}d", getpid());
        return -1;
    }
}

sptr<RSIRenderProcessConnection> RSRenderProcessManager::GetRenderProcessConnectionProxy(pid_t pid)
{
    std::unique_lock<std::mutex> lock(mutex_);
    return renderProcessConnections_[pid];
}

void RSRenderProcessManager::SetRenderProcessConnectionProxy(pid_t pid, sptr<RSIRenderProcessConnection> conn)
{
    renderProcessConnection_[pid] = conn;
}

pid_t RSRenderProcessManager::GetRenderProcessPid(ScreenId screenId)
{
    std::unique_lock<std::mutex> lock(mutex_);
    return screenId2RenderProcess_[screenId];
}

void RSRenderProcessManager::RSScreenChangeHandler::OnScreenChanged(ScreenId id, ScreenEvent event, ScreenChangeReason reson)
{
    if (event == ScreenEvent::CONNECTED) {
        connectCallback_(id, event, reason);
    }
}
} // namespace Rosen
} // namespace OHOS